// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ast

import (
	"fmt"
)

func ForEachChild(node Node, cbNode func(node Node) bool, cbNodes func(list List) bool) bool {
	var stop = false
	visit := func(node Node) {
		if !isNil(node) && !stop {
			stop = cbNode(node)
		}
	}

	visitList := func(list List) {
		if isNil(list) || stop {
			return
		}

		if cbNodes != nil {
			stop = cbNodes(list)
		} else {
			for i := 0; i < list.Len(); i++ {
				stop = cbNode(list.NodeAt(i))
			}
		}
		return
	}


	// walk children
	// (the order of the cases matches the order
	// of the corresponding node types in ast.go)
	switch n := node.(type) {
	case *TokenNode:
		// nothing to do

	case *Modifier:
		// nothing to do

	case *Annotation:
		visit(n.Type)
		visitList(n.Arguments)

	case *SyntaxList:
		// nothing to do

	case *QualifiedName:
		visit(n.Left)
		visit(n.Right)

	case *HeritageClause:
		visitList(n.Types)

	case *CaseClause:
		visit(n.Token)
		visit(n.Expression)
		visitList(n.Statements)

	case *CatchClause:
		visit(n.VariableDeclaration)
		visit(n.Block)

	case *CaseBlock:
		visitList(n.Clauses)

	case *BadDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)

	case *PackageDeclaration:
		visit(n.Name)

	case *ImportDeclaration:
		visit(n.Path)
		visit(n.Target)

	case *VariableDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visit(n.Name)
		visit(n.Type)
		visit(n.Initializer)

	case *DocumentTypeDeclaration:
		visit(n.Dots)
		visit(n.Type)

	case *ParameterDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visit(n.Name)
		visit(n.Type)
		visit(n.Initializer)
		visit(n.Dots)

	case *TypeParameterDeclaration:
		visit(n.Name)
		visit(n.Constraint)

	case *ArrowFunctionDeclaration:
		visitList(n.TypeParameters)
		visitList(n.Parameters)
		visit(n.Type)
		visitList(n.Throws)
		visit(n.Arrow)
		visit(n.Body)

	case *ConstructorDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visitList(n.TypeParameters)
		visitList(n.Parameters)
		visit(n.Type)
		visitList(n.Throws)
		visit(n.Body)

	case *FieldDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visit(n.Name)
		visit(n.Type)
		visit(n.Initializer)

	case *MethodDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visit(n.Name)
		visitList(n.TypeParameters)
		visitList(n.Parameters)
		visit(n.Type)
		visitList(n.Throws)
		visit(n.Body)

	case *ClassDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visit(n.Name)
		visitList(n.TypeParameters)
		visitList(n.HeritageClauses)
		visitList(n.Members)

	case *BodyDeclaration:
		visitList(n.Members)

	case *EnumItemDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visit(n.Name)
		visitList(n.Arguments)

	case *EnumDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visit(n.Name)
		visitList(n.TypeParameters)
		visitList(n.HeritageClauses)
		visitList(n.EnumItems)
		visitList(n.Members)

	case *InterfaceDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visit(n.Name)
		visitList(n.TypeParameters)
		visitList(n.HeritageClauses)
		visitList(n.Members)

	case *AnnotationDeclaration:
		visitList(n.Annotations)
		visitList(n.Modifiers)
		visit(n.Name)
		visitList(n.TypeParameters)
		visitList(n.HeritageClauses)
		visitList(n.Members)

	case *OmittedExpression:
		// nothing to do

	case *Identifier:
		// nothing to do

	case *ArrowExpression:
		visit(n.Declaration)

	case *PrefixUnaryExpression:
		visit(n.Operator)
		visit(n.Operand)

	case *PostfixUnaryExpression:
		visit(n.Operand)
		visit(n.Operator)

	case *BinaryExpression:
		visit(n.Left)
		visit(n.Operator)
		visit(n.Right)

	case *ConditionalExpression:
		visit(n.Condition)
		visit(n.QuestionTok)
		visit(n.WhenTrue)
		visit(n.ColonTok)
		visit(n.WhenFalse)

	case *ThisExpression:
		visit(n.Expression)

	case *SuperExpression:
		visit(n.Expression)

	case *QualifiedExpression:
		visit(n.Type)

	case *ConstructorExpression:
		visit(n.Scope)
		visit(n.TypeName)
		visitList(n.TypeArguments)
		visitList(n.Arguments)

	case *NewObjectExpression:
		visit(n.Type)
		visit(n.Body)

	case *NewArrayExpression:
		visit(n.Type)
		visit(n.Initializer)

	case *AsExpression:
		visit(n.Expression)
		visit(n.Type)

	case *ArrayLiteralExpression:
		visitList(n.Elements)

	case *ParenthesizedExpression:
		visit(n.Expression)

	case *ElementAccessExpression:
		visit(n.Expression)
		visit(n.ArgumentExpression)

	case *LiteralExpression:
		// nothing to do

	case *ClassExpression:
		visit(n.Type)

	case *InstanceAccessExpression:
		visit(n.Left)
		visit(n.Right)

	case *SelectorExpression:
		visit(n.Expression)
		visit(n.Name)

	case *CallExpression:
		visit(n.Expression)
		visitList(n.TypeArguments)
		visitList(n.Arguments)

	case *DocumentCallExpression:
		visit(n.Expression)
		visitList(n.Arguments)

	case *EmptyStatement:
		// nothind to do

	case *DeclarationStatement:
		visit(n.Declaration)

	case *ExpressionStatement:
		visit(n.Expression)

	case *BlockStatement:
		visitList(n.Statements)

	case *TryStatement:
		visit(n.TryBlock)
		visitList(n.CatchClauses)
		visit(n.FinallyBlock)

	case *IfStatement:
		visit(n.Condition)
		visit(n.ThenStatement)
		visit(n.ElseStatement)

	case *DoStatement:
		visit(n.Body)
		visit(n.Condition)

	case *WhileStatement:
		visit(n.Condition)
		visit(n.Body)

	case *ForStatement:
		visit(n.Initializer)
		visit(n.Condition)
		visit(n.Incrementor)
		visit(n.Body)

	case *ForOfStatement:
		visit(n.VariableDeclaration)
		visit(n.Expression)
		visit(n.Body)

	case *BranchStatement:
		visit(n.Label)

	case *ThrowStatement:
		visit(n.Expression)

	case *ReturnStatement:
		visit(n.Expression)

	case *SwitchStatement:
		visit(n.Expression)
		visit(n.Body)

	case *LabeledStatement:
		visit(n.Label)
		visit(n.Statement)

	case *BoundsType:
		visit(n.Bound)
		visit(n.Type)

	case *IntrinsicType:
		// nothing to do

	case *TypeReference:
		visit(n.Name)
		visitList(n.TypeArguments)

	case *ArrayType:
		visit(n.Element)
		visit(n.Dimension)

	case *IntersectionType:
		visitList(n.Types)

	case *UnionType:
		visitList(n.Types)

	case *DocRoot:
		visitList(n.Children)

	case *Number:
		// Nothing to do

	case *Version:
		visit(n.Left)
		visit(n.Right)

	case *PlainText:
		// Nothing to do

	case *Header:
		visit(n.Text)

	case *Bold:
		visit(n.Text)

	case *Italic:
		visit(n.Text)

	case *BoldItalic:
		visit(n.Text)

	case *Paragraph:
		visitList(n.Content)

	case *CodeText:
		visit(n.Content)

	case *CodeBlock:
		visit(n.Language)
		visitList(n.Content)

	case *CodeLink:
		visit(n.Name)
		visit(n.Expression)

	case *Link:
		visit(n.Name)
		visit(n.Address)

	case *LineBreak:
		// Nothing to do

	case *OrderedList:
		visitList(n.Content)

	case *UnorderedList:
		visitList(n.Content)

	case *ListItem:
		visit(n.Content)

	case *AuthorTag:
		visit(n.Content)

	case *AliasTag:
		visit(n.Name)
		visit(n.Content)

	case *ParameterTag:
		visit(n.Name)
		visit(n.Content)

	case *ReturnTag:
		visit(n.Content)

	case *ExceptionTag:
		visit(n.Type)
		visit(n.Content)

	case *SeeTag:
		visit(n.Expression)
		visit(n.Content)

	case *SinceTag:
		visit(n.Version)
		visit(n.Content)

	case *VersionTag:
		visit(n.Version)
		visit(n.Content)

	case *DeprecatedTag:
		visit(n.Content)

	case *SourceFile:
		visit(n.PackageDeclaration)
		visitList(n.Imports)
		visitList(n.Declarations)

	case *Package:
		for _, file := range n.Files {
			visit(file)
		}

	default:
		panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
	}

	return stop
}
